package glossos.tree.snapshot;

import static org.hibernate.criterion.Restrictions.eq;
import glossos.tree.Node;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Collection;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.jmock.lib.legacy.ClassImposteriser;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public class HibernateNodeRepository extends HibernateDaoSupport implements NodeRepository{
    private Long nextId;
    private long updateBufferSize = 0L;

    public HibernateNodeRepository(){
        logger.debug(this.getClass() + " successfully created.");
    }

    public void save(Node node) {
    	getHibernateTemplate().saveOrUpdate(node);
    	updateBufferSize++;
    	if (updateBufferSize % 20 == 0){
    		getSession().flush();
    		getSession().clear();
    	}

	}
	
	public Node findById(Long id) {
		try {
			Node result = (Node)getHibernateTemplate().load(Node.class, id);
			result.setParent(createNodeProxy(result.getParentId()));
			return result;
		} catch (EmptyResultDataAccessException e){
			return null;
		}
	}

	@SuppressWarnings("unchecked")
	public Set<Node> findByParentId(Long parentId) {
		DetachedCriteria criteria = DetachedCriteria
										.forClass(Node.class)
										.add(eq("parentId", parentId));
		List<Node> nodes = getHibernateTemplate().findByCriteria(criteria);
		Node parent = createNodeProxy(parentId);
		for (Node node : nodes) {
			node.setParent(parent);
		}
		return new HashSet<Node>(nodes);
	}

	public Node findByParentIdAndWord(Long parentId, String word) {
		DetachedCriteria criteria = DetachedCriteria
										.forClass(Node.class)
										.add(eq("parentId", parentId))
										.add(eq("word", word));
		try {
			List<Node> nodes = getHibernateTemplate().findByCriteria(criteria);
			if (nodes.isEmpty())
				return null;
			Node result = nodes.get(0);
			result.setParent(createNodeProxy(parentId));
			return result;
		} catch (EmptyResultDataAccessException e){
			return null;
		}
	}

    private Node createNodeProxy(Long parentId) {
    	return parentId != null && parentId != 0
    		? ClassImposteriser.INSTANCE.imposterise(new NodeProxy(this, parentId), Node.class)
    		: null;
    }
	
	public Long nextId() {
        if (nextId == null) {
			DetachedCriteria criteria = DetachedCriteria
											.forClass(Node.class)
											.setProjection(Projections.max("id"));
			nextId = (Long)getHibernateTemplate().findByCriteria(criteria).get(0);
			if (nextId == null)
				nextId = 1L;
		}
        return ++nextId;
    }

    public Long count() {
		DetachedCriteria criteria = DetachedCriteria
										.forClass(Node.class)
										.setProjection(Projections.count("id"));

        return (Long)getHibernateTemplate().findByCriteria(criteria).get(0);
    }

    public void removeAll() {
        Collection nodes = getHibernateTemplate().findByCriteria(DetachedCriteria.forClass(Node.class));
        getHibernateTemplate().deleteAll(nodes);
    }
}
